﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;

namespace Uhi.Applications.FileAid.View
{
    #region TextEditor
    /// <summary>
    /// 固定長テキスト編集用のコントロール。
    /// </summary>
    [TemplatePart(Name = "ContentHolder", Type = typeof(ScrollViewer))]
    [TemplateVisualState(Name="Focused", GroupName="FocusStates")]
    [TemplateVisualState(Name="Unfocused", GroupName="FocusStates")]
    public class TextEditor : Control
    {
        #region _cctor
        /// <summary>
        /// <see cref="TextEditor"/> を初期化する。
        /// </summary>
        static TextEditor()
        {
            Control.FontFamilyProperty.OverrideMetadata(typeof(TextEditor), new FrameworkPropertyMetadata(TextChanged));
            Control.FontStyleProperty.OverrideMetadata(typeof(TextEditor), new FrameworkPropertyMetadata(TextChanged));
            Control.FontWeightProperty.OverrideMetadata(typeof(TextEditor), new FrameworkPropertyMetadata(TextChanged));
            Control.FontStretchProperty.OverrideMetadata(typeof(TextEditor), new FrameworkPropertyMetadata(TextChanged));
            Control.FontSizeProperty.OverrideMetadata(typeof(TextEditor), new FrameworkPropertyMetadata(TextChanged));
            Control.ForegroundProperty.OverrideMetadata(typeof(TextEditor), new FrameworkPropertyMetadata(TextChanged));
        }
        #endregion

        #region _ctor
        /// <summary>
        /// <see cref="TextEditor"/> の新しいインスタンスを生成する。
        /// </summary>
        public TextEditor()
        {
            this.DefaultStyleKey = typeof(TextEditor);
            this.InputMask = new List<MaskingItem>();
        }
        #endregion

        #region ContentHolder
        /// <summary>
        /// テキストの配置先のオブジェクト。
        /// </summary>
        private ScrollViewer ContentHolder { get; set; }
        #endregion

        #region OnApplyTemplate

        /// <summary>
        /// テキスト表示用の <see cref="TextView"/>。
        /// </summary>
        private TextView view;

        /// <summary>
        /// テキストボックスのキャレット。
        /// </summary>
        private Caret caret;

        /// <summary>
        /// テキストボックス内の特定の文字列をハイライトするための <see cref="Adorner"/>。
        /// </summary>
        private Marker marker;

        /// <summary>
        /// IME の変換候補ウィンドウの表示位置を制御するオブジェクト。
        /// </summary>
        private ImeSupport ime;

        /// <summary>
        /// アプリケーションコード、または内部プロセスが
        /// <see cref="FrameworkElement.ApplyTemplate"/> をダイヤルするたびに呼び出される。
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.ContentHolder = this.GetTemplateChild("ContentHolder") as ScrollViewer;

            this.view = new TextView(this);
            this.ContentHolder.Content = this.view;
            this.ContentHolder.Focusable = false;

            this.caret = new Caret(this.view, this);
            this.marker = new Marker(this.view, this);
            this.view.Loaded += (obj, e) =>
            {
                var layer = AdornerLayer.GetAdornerLayer(this.view);
                layer.Add(this.caret);
                layer.Add(this.marker);
            };
            
            this.UpdateState(false);
        }

        /// <summary>
        /// VisualState を更新する。
        /// </summary>
        /// <param name="useTransitions"><see cref="VisualTransition"/> を使用して状態を遷移させる場合は true、使用しない場合は false。</param>
        private void UpdateState(bool useTransitions)
        {
            if (this.IsKeyboardFocused)
                VisualStateManager.GoToState(this, "Focused", useTransitions);
            else
                VisualStateManager.GoToState(this, "Unfocused", useTransitions);

            this.InvalidateVisual();
            if (this.view != null)
                this.view.InvalidateVisual();
            if (this.caret != null)
                this.caret.InvalidateVisual();
            if (this.marker != null)
                this.marker.InvalidateVisual();

            if (this.ime == null)
                this.ime = new ImeSupport(this);

            if (this.ContentHolder != null && this.IsMeasureValid && this.Text != null)
            {
                var selected = this.Text[this.CaretIndex];
                var origin = this.GetCaretPosition();
                var size = this.MeasureString(selected);

                if (this.ContentHolder.ScrollableWidth > 0)
                {
                    // ContentHolder をスクロール
                    if (origin.X < this.ContentHolder.HorizontalOffset)
                        this.ContentHolder.PageLeft();
                    if (origin.X + size.Width > this.ActualWidth + this.ContentHolder.HorizontalOffset)
                        this.ContentHolder.PageRight();
                }
                else
                {
                    // 上位のコンテナをスクロール
                    this.BringIntoView(new Rect(origin, size));
                }
            }
        }

        #endregion

        #region RowCount

        /// <summary>
        /// <see cref="RowCount"/> を識別する依存関係プロパティ。
        /// </summary>
        public static readonly DependencyProperty RowCountProperty =
            DependencyProperty.Register(
                "RowCount",
                typeof(int),
                typeof(TextEditor),
                new PropertyMetadata(1, TextChanged)
            );

        /// <summary>
        /// テキストの行数を取得、または設定する。
        /// </summary>
        public int RowCount
        {
            get { return (int)this.GetValue(RowCountProperty); }
            set { this.SetValue(RowCountProperty, value); }
        }

        #endregion

        #region Text

        /// <summary>
        /// <see cref="Text"/> を識別する依存関係プロパティ。
        /// </summary>
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register(
                "Text",
                typeof(string),
                typeof(TextEditor),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, TextChanged)
            );

        /// <summary>
        /// テキストを取得、または設定する。
        /// </summary>
        public string Text
        {
            get { return this.GetValue(TextProperty) as string; }
            set { this.SetValue(TextProperty, value); }
        }

        /// <summary>
        /// テキストの表示に関係するプロパティが変更されたときに呼び出される。
        /// </summary>
        /// <param name="obj">イベントソース。</param>
        /// <param name="e">イベントデータ。</param>
        private static void TextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var editor = obj as TextEditor;

            editor.FormattedText =
                Enumerable.Range(0, editor.RowCount)
                .Select(i => editor.GetTextAt(i))
                .Select(text => editor.Format(text))
                .ToArray();

            editor.UpdateState(true);
        }

        /// <summary>
        /// 装飾済みのテキストを取得、または設定する。
        /// </summary>
        private FormattedText[] FormattedText { get; set; }

        #region GetTextAt
        /// <summary>
        /// 指定した行の文字列を取得する。
        /// </summary>
        /// <param name="row">取得する行。</param>
        /// <returns>指定した行の文字列。</returns>
        private string GetTextAt(int row)
        {
            return this.Text == null ?
                "" :
                string.Concat(
                    Enumerable.Range(0, this.Text.Length)
                    .Where(i => i % this.RowCount == row)
                    .Select(i => this.Text[i])
                );
        }
        #endregion

        #region Format
        /// <summary>
        /// 文字列を装飾する。
        /// </summary>
        /// <param name="text">装飾する文字列。</param>
        /// <returns>装飾された文字列。</returns>
        private FormattedText Format(string text)
        {
            return new FormattedText(
                this.Mask(text ?? ""),
                CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, this.FontStretch),
                this.FontSize,
                this.Foreground
            );
        }
        #endregion

        #region MeasureString

        /// <summary>
        /// 文字列のサイズを計測する。
        /// </summary>
        /// <param name="text">計測する文字列。</param>
        /// <returns>文字列のサイズ。</returns>
        private Size MeasureString(string text)
        {
            var formatted = this.Format(text);
            return new Size(formatted.WidthIncludingTrailingWhitespace, formatted.Height);
        }

        /// <summary>
        /// 文字列のサイズを計測する。
        /// </summary>
        /// <param name="c">計測する文字。</param>
        /// <returns>文字列のサイズ。</returns>
        private Size MeasureString(char c)
        {
            return this.MeasureString(c.ToString());
        }

        #endregion

        #endregion

        #region CaretIndex

        /// <summary>
        /// <see cref="CaretIndex"/> を識別する依存関係プロパティ。
        /// </summary>
        public static readonly DependencyProperty CaretIndexProperty =
            DependencyProperty.Register(
                "CaretIndex",
                typeof(int),
                typeof(TextEditor),
                new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CaretIndexChanged, CoerceCaretIndex)
            );

        /// <summary>
        /// キャレットの位置を取得、または設定する。
        /// </summary>
        public int CaretIndex
        {
            get { return (int)this.GetValue(CaretIndexProperty); }
            set { this.SetValue(CaretIndexProperty, value); }
        }

        /// <summary>
        /// <see cref="CaretIndex"/> が変更されたときに呼び出される。
        /// </summary>
        /// <param name="obj">イベントソース。</param>
        /// <param name="e">イベントデータ。</param>
        private static void CaretIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            (obj as TextEditor).UpdateState(true);
        }

        /// <summary>
        /// <see cref="CaretIndex"/> の値を強制する。
        /// </summary>
        /// <param name="obj">プロパティが変更されたオブジェクト。</param>
        /// <param name="value">強制が試行される前のプロパティ値。</param>
        /// <returns>強制された適切な値。</returns>
        private static object CoerceCaretIndex(DependencyObject obj, object value)
        {
            var editor = obj as TextEditor;
            var index = (int)value;

            if (editor.Text == null)
                return 0;
            else if (index >= editor.Text.Length)
                return editor.Text.Length - 1;
            else if (index < 0)
                return 0;
            else
                return index;
        }

        #region GetCaretIndexAt
        /// <summary>
        /// 指定した座標に対応するキャレット位置を取得する。
        /// </summary>
        /// <param name="point">任意の座標。</param>
        /// <returns>キャレット位置。</returns>
        private int GetCaretIndexAt(Point point)
        {
            var x = 0;
            var y = 0;
            var width = 0.0;
            var height = 0.0;

            for (int i = 0; i < this.RowCount; i++)
            {
                y = i;
                height += this.FormattedText[i].Height;
                if (point.Y < height)
                    break;
            }

            var text = this.GetTextAt(y);
            for (int i = 0; i < text.Length; i++)
            {
                x = i;
                width += this.MeasureString(text[i]).Width;
                if (point.X < width)
                    break;
            }

            return this.RowCount * x + y;
        }
        #endregion

        #region GetCaretPosition
        /// <summary>
        /// キャレットの座標を取得する。
        /// </summary>
        /// <returns>キャレットの座標。</returns>
        private Point GetCaretPosition()
        {
            return this.GetCaretPositionAt(this.CaretIndex);
        }
        #endregion

        #region GetCaretPositionAt
        /// <summary>
        /// 指定したキャレット位置の座標を取得する。
        /// </summary>
        /// <param name="index">任意のキャレット位置。</param>
        /// <returns>指定したキャレット位置の座標。</returns>
        private Point GetCaretPositionAt(int index)
        {
            var x = (int)(index / this.RowCount);
            var y = index % this.RowCount;

            var text = this.GetTextAt(y);
            var posX = this.MeasureString(text.Substring(0, x)).Width;
            var posY = this.FormattedText.Take(y).Sum(f => f.Height);

            return new Point(posX, posY);
        }
        #endregion

        #endregion

        #region CaretColor

        /// <summary>
        /// <see cref="CaretColor"/> を識別する依存関係プロパティ。
        /// </summary>
        public static readonly DependencyProperty CaretColorProperty =
            DependencyProperty.Register(
                "CaretColor",
                typeof(Color),
                typeof(TextEditor),
                new PropertyMetadata(SystemColors.HighlightColor, CaretColorChanged)
            );

        /// <summary>
        /// キャレットの色を取得、または設定する。
        /// </summary>
        public Color CaretColor
        {
            get { return (Color)this.GetValue(CaretColorProperty); }
            set { this.SetValue(CaretColorProperty, value); }
        }

        /// <summary>
        /// <see cref="CaretColor"/> が変更されたときに呼び出される。
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="e"></param>
        private static void CaretColorChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            (obj as TextEditor).UpdateState(true);
        }

        #endregion

        #region CaretOpacity

        /// <summary>
        /// <see cref="CaretOpacity"/> を識別する依存関係プロパティ。
        /// </summary>
        public static readonly DependencyProperty CaretOpacityProperty =
            DependencyProperty.Register(
                "CaretOpacity",
                typeof(double),
                typeof(TextEditor),
                new PropertyMetadata(0.5, CaretOpacityChanged)
            );

        /// <summary>
        /// キャレットの透明度を取得、または設定する。
        /// </summary>
        public double CaretOpacity
        {
            get { return (double)this.GetValue(CaretOpacityProperty); }
            set { this.SetValue(CaretOpacityProperty, value); }
        }

        /// <summary>
        /// <see cref="CaretOpacity"/> が変更されたときに呼び出される。
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="e"></param>
        private static void CaretOpacityChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            (obj as TextEditor).UpdateState(true);
        }

        #endregion

        #region MouseEvents

        /// <summary>
        /// <see cref="UIElement.MouseLeftButtonDown"/> イベントが発生する前に呼び出される。
        /// </summary>
        /// <param name="e">イベントデータ。</param>
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);

            e.Handled = true;
            this.UpdateCaretIndex(e.GetPosition(this));
        }

        /// <summary>
        /// <see cref="UIElement.MouseRightButtonDown"/> イベントが発生する前に呼び出される。
        /// </summary>
        /// <param name="e">イベントデータ。</param>
        protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseRightButtonDown(e);

            e.Handled = true;
            this.UpdateCaretIndex(e.GetPosition(this));
        }

        /// <summary>
        /// マウスボタン入力に応じてキャレット位置を更新する。
        /// </summary>
        /// <param name="position">マウスの入力座標。</param>
        private void UpdateCaretIndex(Point position)
        {
            this.Focus();
            this.CaretIndex = this.GetCaretIndexAt(position);
        }

        #endregion

        #region KeyEvents

        /// <summary>
        /// <see cref="UIElement.TextInput"/> イベントが発生する前に呼び出される。
        /// </summary>
        /// <param name="e">イベントデータ。</param>
        protected override void OnTextInput(TextCompositionEventArgs e)
        {
            base.OnTextInput(e);
            this.UpdateText(e.Text);
        }

        /// <summary>
        /// 入力された文字列で現在位置の文字列を置き換える。
        /// </summary>
        /// <param name="text">入力テキスト。</param>
        private void UpdateText(string text)
        {
            this.Text =
                this.Text
                .Remove(this.CaretIndex, text.Length)
                .Insert(this.CaretIndex, text);

            this.CaretIndex += text.Length;
        }

        /// <summary>
        /// <see cref="UIElement.PreviewKeyDown"/> イベントが発生する前に呼び出される。
        /// </summary>
        /// <param name="e">イベントデータ。</param>
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);

            e.Handled = this.UpdateCaretIndex(e.Key, Keyboard.Modifiers);
        }

        /// <summary>
        /// キー入力に応じてキャレット位置を更新する。
        /// </summary>
        /// <param name="key">入力された <see cref="Key"/>。</param>
        /// <param name="modifier">入力されている装飾キー。</param>
        /// <remarks>キーが処理された場合 true、処理されなかった場合 false。</remarks>
        private bool UpdateCaretIndex(Key key, ModifierKeys modifier)
        {
            switch (key)
            {
                case Key.Right:
                    if (this.CaretIndex + this.RowCount <= this.Text.Length - 1)
                        this.CaretIndex += this.RowCount;
                    return true;

                case Key.Left:
                    if (this.CaretIndex - this.RowCount >= 0)
                        this.CaretIndex -= this.RowCount;
                    return true;

                case Key.Up:
                    this.CaretIndex -= 1;
                    return true;

                case Key.Down:
                    this.CaretIndex += 1;
                    return true;

                case Key.Home:
                    if (modifier.HasFlag(ModifierKeys.Control))
                        this.CaretIndex = 0;
                    else
                        this.CaretIndex = this.CaretIndex % this.RowCount;
                    return true;

                case Key.End:
                    if (modifier.HasFlag(ModifierKeys.Control))
                        this.CaretIndex = this.Text.Length - 1;
                    else
                        this.CaretIndex = (this.Text.Length - 1) - this.CaretIndex % this.RowCount;
                    return true;

                case Key.Escape:
                    return true;

                case Key.Back:
                    this.CaretIndex -= 1;
                    return true;

                default:
                    return false;

            }
        }

        #endregion

        #region FocusEvents

        /// <summary>
        /// <see cref="UIElement.GotFocus"/> イベントが発生する前に呼び出される。
        /// </summary>
        /// <param name="e">イベントデータ。</param>
        protected override void OnGotFocus(RoutedEventArgs e)
        {
            base.OnGotFocus(e);
            this.UpdateState(true);
        }

        /// <summary>
        /// <see cref="UIElement.LostFocus"/> イベントが発生する前に呼び出される。
        /// </summary>
        /// <param name="e">イベントデータ。</param>
        protected override void OnLostFocus(RoutedEventArgs e)
        {
            base.OnLostFocus(e);
            this.UpdateState(true);
        }

        #endregion

        #region Highlight

        #region HighlightedText

        /// <summary>
        /// <see cref="HighlightedText"/> を識別する依存関係プロパティ。
        /// </summary>
        public static readonly DependencyProperty HighlightedTextProperty =
            DependencyProperty.Register(
                "HighlightedText",
                typeof(string),
                typeof(TextEditor),
                new PropertyMetadata(HighlightedTextChanged)
            );

        /// <summary>
        /// ハイライトする文字列を取得、または設定する。
        /// </summary>
        public string HighlightedText
        {
            get { return this.GetValue(HighlightedTextProperty) as string; }
            set { this.SetValue(HighlightedTextProperty, value); }
        }

        /// <summary>
        /// <see cref="HighlightedText"/> が変更されたときに呼び出される。
        /// </summary>
        /// <param name="obj">イベントソース。</param>
        /// <param name="e">イベントデータ。</param>
        private static void HighlightedTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var editor = obj as TextEditor;
            editor.ClearHighlights();
            editor.Highlight(e.NewValue as string);
        }

        #endregion

        #region HighlightColor

        /// <summary>
        /// <see cref="HighlightColor"/> を識別する依存関係プロパティ。
        /// </summary>
        public static readonly DependencyProperty HighlightColorProperty =
            DependencyProperty.Register(
                "HighlightColor",
                typeof(Color),
                typeof(TextEditor),
                new PropertyMetadata(Colors.Yellow)
            );

        /// <summary>
        /// ハイライトの色を取得、または設定する。
        /// </summary>
        public Color HighlightColor
        {
            get { return (Color)this.GetValue(HighlightColorProperty); }
            set { this.SetValue(HighlightColorProperty, value); }
        }

        #endregion

        #region HighlightOpacity

        /// <summary>
        /// <see cref="HighlightOpacity"/> を識別する依存関係プロパティ。
        /// </summary>
        public static readonly DependencyProperty HighlightOpacityProperty =
            DependencyProperty.Register(
                "HighlightOpacity",
                typeof(double),
                typeof(TextEditor),
                new PropertyMetadata(0.5, null, CoerceHighlightOpacity)
            );

        /// <summary>
        /// ハイライトの透明度を取得、または設定する。
        /// </summary>
        public double HighlightOpacity
        {
            get { return (double)this.GetValue(HighlightOpacityProperty); }
            set { this.SetValue(HighlightOpacityProperty, value); }
        }

        /// <summary>
        /// <see cref="HighlightOpacity"/> の値を強制する。
        /// </summary>
        /// <param name="obj">プロパティが変更されたオブジェクト。</param>
        /// <param name="value">強制が試行される前のプロパティ値。</param>
        /// <returns>強制された適切な値。</returns>
        private static object CoerceHighlightOpacity(DependencyObject obj, object value)
        {
            var editor = obj as TextEditor;
            var opacity = (double)value;

            if (opacity > 1.0)
                return 1.0;
            else if (opacity < 0)
                return 0.0;
            else
                return opacity;
        }

        #endregion

        /// <summary>
        /// <see cref="Text"/> 中の指定した文字列の位置とサイズのセット。
        /// </summary>
        private Rect[] markerBounds = new Rect[0];

        /// <summary>
        /// 指定した文字列をハイライトする。
        /// </summary>
        /// <param name="text">検索対象の文字列。</param>
        public void Highlight(string text)
        {
            this.markerBounds = this.markerBounds.Concat(this.GetTextBounds(text)).ToArray();
            this.UpdateState(true);
        }

        /// <summary>
        /// すべてのハイライトを消去する。
        /// </summary>
        public void ClearHighlights()
        {
            this.markerBounds = new Rect[0];
        }

        /// <summary>
        /// <see cref="Text"/> 中の指定した文字列の位置とサイズのセットを取得する。
        /// </summary>
        /// <param name="text">検索対象の文字列。</param>
        /// <returns>指定した文字列の位置とサイズのセット。</returns>
        private Rect[] GetTextBounds(string text)
        {
            if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(this.Text))
                return new Rect[0];

            var regex = new Regex(text);
            var matches = regex.Matches(this.Text);

            var indexes = this.RowCount == 1
                ? matches.AsEnumerable().Select(m => new { Index = m.Index, Length = m.Length })
                : matches.AsEnumerable().SelectMany(m => Enumerable.Range(m.Index, text.Length)).Select(i => new { Index = i, Length = 1 });

            return indexes
                .Select(i => new { Point = this.GetCaretPositionAt(i.Index), Size = this.MeasureString(this.Text.Substring(i.Index, i.Length)) })
                .Select(p => new Rect(p.Point, p.Size))
                .ToArray();
        }

        #endregion

        #region InputMask

        /// <summary>
        /// <see cref="InputMask"/> を識別する依存関係プロパティ。
        /// </summary>
        public static readonly DependencyProperty InputMaskProperty =
            DependencyProperty.Register(
                "InputMask",
                typeof(List<MaskingItem>),
                typeof(TextEditor)
            );

        /// <summary>
        /// マスキング表示する文字列の情報を取得、または設定する。
        /// </summary>
        /// <remarks>
        /// <see cref="MaskingItem.MaskPattern"/> で指定された文字列を
        /// <see cref="MaskingItem.MaskingChar"/> で置き換えて表示する。
        /// </remarks>
        public List<MaskingItem> InputMask
        {
            get { return this.GetValue(InputMaskProperty) as List<MaskingItem>; }
            set { this.SetValue(InputMaskProperty, value); }
        }

        /// <summary>
        /// 入力文字列をマスクする。
        /// </summary>
        /// <param name="text">マスクする文字列。</param>
        /// <returns>マスキング結果。</returns>
        private string Mask(string text)
        {
            var ret = text;
            this.InputMask.ForEach(m =>
            {
                var regex = new Regex(m.MaskPattern);
                ret = regex.Replace(ret, m.MaskingChar.ToString());
            });

            return ret;
        }

        #endregion

        #region TextView
        /// <summary>
        /// テキスト表示用のコントロール。
        /// </summary>
        private class TextView : FrameworkElement
        {
            #region _ctor
            /// <summary>
            /// <see cref="TextView"/> の新しいインスタンスを生成する。
            /// </summary>
            /// <param name="owner">この <see cref="TextView"/> を所有する <see cref="TextEditor"/>。</param>
            public TextView(TextEditor owner)
            {
                this.Owner = owner;
            }
            #endregion

            #region Owner
            /// <summary>
            /// この <see cref="TextView"/> を所有する <see cref="TextEditor"/>。
            /// </summary>
            private TextEditor Owner { get; set; }
            #endregion

            #region MeasureOverride
            /// <summary>
            /// 子要素に必要なレイアウトのサイズを測定し、パネルのサイズを決定する。
            /// </summary>
            /// <param name="availableSize">子要素に与えることができる使用可能なサイズ。</param>
            /// <returns>レイアウト時にこのパネルが必要とするサイズ。</returns>
            protected override Size MeasureOverride(Size availableSize)
            {
                if (this.Owner.FormattedText == null)
                    return Size.Empty;

                var w = this.Owner.FormattedText.Max(f => f.WidthIncludingTrailingWhitespace);
                var h = this.Owner.FormattedText.Sum(f => f.Height);

                return new Size(w, h);
            }
            #endregion

            #region OnRender
            /// <summary>
            /// レイアウトシステムから指示されるレンダリング操作に参加する。
            /// </summary>
            /// <param name="drawingContext">特定の要素に対する描画命令。</param>
            protected override void OnRender(DrawingContext drawingContext)
            {
                base.OnRender(drawingContext);

                drawingContext.DrawRectangle(this.Owner.Background, null, new Rect(new Point(0, 0), this.DesiredSize));

                if (this.Owner.FormattedText == null)
                    return;

                var y = 0.0;
                this.Owner.FormattedText.ToList().ForEach(f =>
                {
                    drawingContext.DrawText(f, new Point(0, y));
                    y += f.Height;
                });
            }
            #endregion

            #region HitTestCore
            /// <summary>
            /// HitTest を実行する。
            /// </summary>
            /// <param name="hitTestParameters">HitTest に使用するパラメータ。</param>
            /// <returns>HitTest 結果。</returns>
            /// <remarks>
            /// この <see cref="TextView"/> 上で発生した MouseWheel などのイベントが
            /// <see cref="Owner"/> で処理されるように HitTest 結果をリダイレクトする。
            /// </remarks>
            protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
            {
                return new PointHitTestResult(this.Owner, hitTestParameters.HitPoint);;
            }
            #endregion
        }
        #endregion

        #region Caret
        /// <summary>
        /// テキストボックスのキャレット。
        /// </summary>
        private class Caret : Adorner
        {
            #region _ctor
            /// <summary>
            /// <see cref="Caret"/> の新しいインスタンスを生成する。
            /// </summary>
            /// <param name="adornedElement">装飾のバインド先の要素。</param>
            /// <param name="owner">この <see cref="Caret"/> を所有する <see cref="TextEditor"/>。</param>
            public Caret(UIElement adornedElement, TextEditor owner)
                : base(adornedElement)
            {
                this.Owner = owner;
            }
            #endregion

            #region Owner
            /// <summary>
            /// この <see cref="Caret"/> を所有する <see cref="TextEditor"/>。
            /// </summary>
            private TextEditor Owner { get; set; }
            #endregion

            #region OnRender
            /// <summary>
            /// レイアウトシステムから指示されるレンダリング操作に参加する。
            /// </summary>
            /// <param name="drawingContext">特定の要素に対する描画命令。</param>
            protected override void OnRender(DrawingContext drawingContext)
            {
                base.OnRender(drawingContext);

                if (!this.Owner.IsKeyboardFocused)
                    return;

                var selected = this.Owner.Text[this.Owner.CaretIndex];
                var origin = this.Owner.GetCaretPosition();
                var size = this.Owner.MeasureString(selected);

                var brush = new SolidColorBrush(this.Owner.CaretColor);
                brush.Opacity = this.Owner.CaretOpacity;
                brush.Freeze();

                drawingContext.DrawRectangle(brush, null, new Rect(origin, size));
            }
            #endregion
        }
        #endregion

        #region Marker
        /// <summary>
        /// テキストボックス内の特定の文字列をハイライトするための <see cref="Adorner"/>。
        /// </summary>
        private class Marker : Adorner
        {
            #region _ctor
            /// <summary>
            /// <see cref="Marker"/> の新しいインスタンスを生成する。
            /// </summary>
            /// <param name="adornedElement">装飾のバインド先の要素。</param>
            /// <param name="owner">この <see cref="Marker"/> を所有する <see cref="TextEditor"/>。</param>
            public Marker(UIElement adornedElement, TextEditor owner)
                : base(adornedElement)
            {
                this.Owner = owner;
            }
            #endregion

            #region Owner
            /// <summary>
            /// この <see cref="Marker"/> を所有する <see cref="TextEditor"/>。
            /// </summary>
            private TextEditor Owner { get; set; }
            #endregion

            #region OnRender
            /// <summary>
            /// レイアウトシステムから指示されるレンダリング操作に参加する。
            /// </summary>
            /// <param name="drawingContext">特定の要素に対する描画命令。</param>
            protected override void OnRender(DrawingContext drawingContext)
            {
                base.OnRender(drawingContext);

                var brush = new SolidColorBrush(this.Owner.HighlightColor);
                brush.Opacity = this.Owner.HighlightOpacity;
                brush.Freeze();

                this.Owner.markerBounds.ToList().ForEach(b => drawingContext.DrawRectangle(brush, null, b));
            }
            #endregion
        }
        #endregion

        #region ImeSupport
        /// <summary>
        /// IME の変換候補ウィンドウの表示位置を制御する。
        /// </summary>
        private class ImeSupport : IDisposable
        {
            #region _ctor

            /// <summary>
            /// <see cref="ImeSupport"/> の新しいインスタンスを生成する。
            /// </summary>
            /// <param name="editor">制御対象の <see cref="TextEditor"/>。</param>
            public ImeSupport(TextEditor editor)
            {
                if (editor == null)
                    throw new ArgumentNullException("editor");

                this.Editor = editor;
                editor.GotKeyboardFocus += this.OnGotFocus;
                editor.LostKeyboardFocus += this.OnLostFocus;

                InputMethod.SetIsInputMethodSuspended(editor, true);
                this.CurrentContext = IntPtr.Zero;
            }

            /// <summary>
            /// <see cref="ImeSupport"/> のインスタンスを破棄する。
            /// </summary>
            ~ImeSupport()
            {
                this.Dispose();
            }

            /// <summary>
            /// リソースを解放する。
            /// </summary>
            public void Dispose()
            {
                this.ClearContext();

                this.Editor.GotKeyboardFocus -= this.OnGotFocus;
                this.Editor.LostKeyboardFocus -= this.OnLostFocus;

                GC.SuppressFinalize(this);
            }

            #endregion

            #region Editor
            /// <summary>
            /// 制御対象の <see cref="TextEditor"/>。
            /// </summary>
            private TextEditor Editor { get; set; }
            #endregion

            #region CurrentContext
            /// <summary>
            /// 制御対象の <see cref="TextEditor"/> に関連付けられている入力コンテキスト。
            /// </summary>
            private IntPtr CurrentContext { get; set; }
            #endregion

            #region VisualSource
            /// <summary>
            /// 制御対象の <see cref="TextEditor"/> が表示されているソース。
            /// </summary>
            private HwndSource VisualSource { get; set; }
            #endregion

            #region OnGotFocus
            /// <summary>
            /// キーボードフォーカスを取得したときに呼び出される。
            /// </summary>
            /// <param name="sender">イベントソース。</param>
            /// <param name="e">イベントデータ。</param>
            private void OnGotFocus(object sender, KeyboardFocusChangedEventArgs e)
            {
                this.CreateContext();
            }
            #endregion

            #region OnLostFocus
            /// <summary>
            /// キーボードフォーカスを失うときに呼び出される。
            /// </summary>
            /// <param name="sender">イベントソース。</param>
            /// <param name="e">イベントデータ。</param>
            private void OnLostFocus(object sender, KeyboardFocusChangedEventArgs e)
            {
                if (e.OldFocus == this.Editor && this.CurrentContext != IntPtr.Zero)
                    ImeNativeWrapper.NotifyIme(this.CurrentContext);

                this.ClearContext();
            }
            #endregion

            #region CreateContext
            /// <summary>
            /// 入力コンテキストを取得する。
            /// </summary>
            private void CreateContext()
            {
                this.ClearContext();

                this.VisualSource = PresentationSource.FromVisual(this.Editor) as HwndSource;
                if (this.VisualSource != null)
                {
                    this.CurrentContext = ImeNativeWrapper.GetContext(this.VisualSource);
                    this.VisualSource.AddHook(this.WndProc);

                    var threadManager = ImeNativeWrapper.GetTextFrameworkThreadManager();
                    if (threadManager != null)
                        threadManager.SetFocus(null);
                }
            }
            #endregion

            #region ClearContext
            /// <summary>
            /// 入力コンテキストを解放する。
            /// </summary>
            private void ClearContext()
            {
                if (this.VisualSource != null)
                {
                    ImeNativeWrapper.ReleaseContext(this.VisualSource, this.CurrentContext);
                    this.CurrentContext = IntPtr.Zero;
                    this.VisualSource.RemoveHook(this.WndProc);
                    this.VisualSource = null;
                }
            }
            #endregion

            #region UpdateCompositionWindow

            /// <summary>
            /// IME 変換ウィンドウの位置とフォントを更新する。
            /// </summary>
            private void UpdateCompositionWindow()
            {
                if (this.CurrentContext != IntPtr.Zero)
                {
                    var textViewBounds = this.GetTextViewBounds();
                    var characterBounds = this.GetCharacterBounds();
                    
                    ImeNativeWrapper.SetCompositionFont(this.CurrentContext, this.Editor.FontFamily, characterBounds);
                    ImeNativeWrapper.SetCompositionWindow(this.CurrentContext, textViewBounds, characterBounds);
                }
            }

            /// <summary>
            /// テキスト表示エリアの位置とサイズを取得する。
            /// </summary>
            /// <returns>テキスト表示エリアの位置とサイズ。</returns>
            private Rect GetTextViewBounds()
            {
                var view = this.Editor.view;

                if (this.VisualSource.RootVisual == null || !this.VisualSource.RootVisual.IsAncestorOf(view))
                    return Rect.Empty;

                var displayRect = new Rect(0, 0, view.ActualWidth, view.ActualHeight);

                var rect = view
                    .TransformToAncestor(this.VisualSource.RootVisual)
                    .TransformBounds(displayRect);

                return TransformToDevice(rect, this.VisualSource.RootVisual);
            }

            /// <summary>
            /// 選択されている文字の位置とサイズを取得する。
            /// </summary>
            /// <returns>選択されている文字の位置とサイズ。</returns>
            private Rect GetCharacterBounds()
            {
                if (this.VisualSource.RootVisual == null || !this.VisualSource.RootVisual.IsAncestorOf(this.Editor))
                    return Rect.Empty;

                var selectedChar = this.Editor.Text[this.Editor.CaretIndex];
                var displayRect = new Rect(this.Editor.GetCaretPosition(), this.Editor.MeasureString(selectedChar));
                displayRect.Offset(this.Editor.ContentHolder.HorizontalOffset * -1, 0);

                var rect = this.Editor
                    .TransformToAncestor(this.VisualSource.RootVisual)
                    .TransformBounds(displayRect);

                return TransformToDevice(rect, this.VisualSource.RootVisual);
            }

            /// <summary>
            /// 指定した <see cref="Rect"/> をレンダリング先のデバイスに座標変換する。
            /// </summary>
            /// <param name="rect">変換する <see cref="Rect"/>。</param>
            /// <param name="visual">変換先の <see cref="Visual"/>。</param>
            /// <returns>変換結果の <see cref="Rect"/>。</returns>
            private static Rect TransformToDevice(Rect rect, Visual visual)
            {
                var matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
                return Rect.Transform(rect, matrix);
            }

            #endregion

            #region WndProc
            /// <summary>
            /// ウィンドウへ送信されたメッセージを処理する。
            /// </summary>
            /// <param name="hWnd">ウィンドウハンドル。</param>
            /// <param name="msg">メッセージ。</param>
            /// <param name="wParam">メッセージの追加情報。</param>
            /// <param name="lParam">メッセージの追加情報。</param>
            /// <param name="handled">イベント結果を処理済みとしてマークするか。</param>
            /// <returns>メッセージの処理結果。</returns>
            private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
            {
                switch (msg)
                {
                    case ImeNativeWrapper.WM_INPUTLANGCHANGE:
                        this.ClearContext();
                        this.CreateContext();
                        break;

                    case ImeNativeWrapper.WM_IME_COMPOSITION:
                        this.UpdateCompositionWindow();
                        break;
                }

                return IntPtr.Zero;
            }
            #endregion
        }
        #endregion

        #region ImeNativeWrapper
        /// <summary>
        /// IMM32 API Client を操作するためのラッパークラス。
        /// </summary>
        private static class ImeNativeWrapper
        {
            #region Messages

            /// <summary>
            /// IME で漢字変換が確定したメッセージ。
            /// </summary>
            public const int WM_IME_COMPOSITION = 0x10f;

            /// <summary>
            /// 入力言語が変更されたメッセージ。
            /// </summary>
            public const int WM_INPUTLANGCHANGE = 0x51;

            #endregion

            #region GetContext

            /// <summary>
            /// 指定したウィンドウに関連付けられている入力コンテキストを取得する。
            /// </summary>
            /// <param name="hWnd">入力コンテキストを取得するウィンドウのハンドル。</param>
            /// <returns>入力コンテキストのハンドル。</returns>
            [DllImport("imm32.dll")]
            private static extern IntPtr ImmGetContext(IntPtr hWnd);

            /// <summary>
            /// 指定したウィンドウに関連付けられている入力コンテキストを取得する。
            /// </summary>
            /// <param name="source">入力コンテキストを取得するウィンドウのソース。</param>
            /// <returns>入力コンテキストのハンドル。</returns>
            public static IntPtr GetContext(HwndSource source)
            {
                if (source == null)
                    return IntPtr.Zero;

                return ImmGetContext(source.Handle);
            }

            #endregion

            #region ReleaseContext

            /// <summary>
            /// 入力コンテキストを解放する。
            /// </summary>
            /// <param name="hWnd"><see cref="ImmGetContext"/> を呼び出したときに指定したウィンドウハンドル。</param>
            /// <param name="hIMC">入力コンテキストのハンドル。</param>
            /// <returns>関数が成功した場合 true、失敗した場合 false。</returns>
            [DllImport("imm32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);

            /// <summary>
            /// 入力コンテキストを解放する。
            /// </summary>
            /// <param name="source">入力コンテキストを取得したウィンドウのソース。</param>
            /// <param name="hIMC">入力コンテキストのハンドル。</param>
            /// <returns>関数が成功した場合 true、失敗した場合 false。</returns>
            public static bool ReleaseContext(HwndSource source, IntPtr hIMC)
            {
                return source != null && hIMC != IntPtr.Zero && ImmReleaseContext(source.Handle, hIMC);
            }

            #endregion

            #region SetCompositionWindow

            #region COMPOSITIONFORM
            /// <summary>
            /// 入力ウィンドウの位置、およびその他の関連情報を定義する。
            /// </summary>
            [StructLayout(LayoutKind.Sequential)]
            struct COMPOSITIONFORM
            {
                /// <summary>
                /// 描画スタイル。
                /// </summary>
                public int dwStyle;

                /// <summary>
                /// 描画位置。
                /// </summary>
                public POINT ptCurrentPos;

                /// <summary>
                /// 描画範囲。
                /// </summary>
                public RECT rcArea;
            }
            #endregion

            #region POINT
            /// <summary>
            /// 点の座標を定義する。
            /// </summary>
            [StructLayout(LayoutKind.Sequential)]
            struct POINT
            {
                /// <summary>
                /// 点の X 座標。
                /// </summary>
                public int x;

                /// <summary>
                /// 点の Y 座標。
                /// </summary>
                public int y;
            }
            #endregion

            #region RECT
            /// <summary>
            /// 四角形の左上隅と右下隅の座標を定義する。
            /// </summary>
            [StructLayout(LayoutKind.Sequential)]
            struct RECT
            {
                /// <summary>
                /// 四角形の左上隅の X 座標。
                /// </summary>
                public int left;

                /// <summary>
                /// 四角形の左上隅の Y 座標。
                /// </summary>
                public int top;

                /// <summary>
                /// 四角形の右下隅の X 座標。
                /// </summary>
                public int right;

                /// <summary>
                /// 四角形の右下隅の Y 座標。
                /// </summary>
                public int bottom;
            }
            #endregion

            /// <summary>
            /// 変換ウィンドウの位置を設定する。
            /// </summary>
            /// <param name="hIMC">入力コンテキストのハンドル。</param>
            /// <param name="form">入力ウィンドウの新しい位置およびその他の関連情報。</param>
            /// <returns>関数が成功した場合 true、失敗した場合 false。</returns>
            [DllImport("imm32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool ImmSetCompositionWindow(IntPtr hIMC, ref COMPOSITIONFORM form);
		    
            /// <summary>
            /// 変換ウィンドウの位置を設定する。
            /// </summary>
            /// <param name="hIMC">入力コンテキストのハンドル。</param>
            /// <param name="textViewBounds">テキスト表示エリアの位置とサイズ。</param>
            /// <param name="characterBounds">表示する文字の位置とサイズ。</param>
            /// <returns>関数が成功した場合 true、失敗した場合 false。</returns>
            public static bool SetCompositionWindow(IntPtr hIMC, Rect textViewBounds, Rect characterBounds)
            {
                var form = new COMPOSITIONFORM();
                form.dwStyle = 0x0020;
                form.ptCurrentPos.x = (int)Math.Max(characterBounds.Left, textViewBounds.Left);
                form.ptCurrentPos.y = (int)Math.Max(characterBounds.Top, textViewBounds.Top);
                form.rcArea.left = (int)textViewBounds.Left;
                form.rcArea.top = (int)textViewBounds.Top;
                form.rcArea.right = (int)textViewBounds.Right;
                form.rcArea.bottom = (int)textViewBounds.Bottom;

                return ImmSetCompositionWindow(hIMC, ref form);
            }

            #endregion

            #region SetCompositionFont

            #region LOGFONT
            /// <summary>
            /// フォントの属性を定義する。
            /// </summary>
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            struct LOGFONT
            {
                /// <summary>
                /// 文字の高さ。
                /// </summary>
                public int lfHeight;

                /// <summary>
                /// 平均文字幅。
                /// </summary>
                public int lfWidth;

                /// <summary>
                /// 文字送りの方向と X 軸との角度。
                /// </summary>
                public int lfEscapement;

                /// <summary>
                /// ベースラインと X 軸との角度。
                /// </summary>
                public int lfOrientation;

                /// <summary>
                /// フォントの太さ。
                /// </summary>
                public int lfWeight;

                /// <summary>
                /// イタリック体指定。
                /// </summary>
                public byte lfItalic;

                /// <summary>
                /// 下線付き指定。
                /// </summary>
                public byte lfUnderline;

                /// <summary>
                /// 打ち消し線付き指定。
                /// </summary>
                public byte lfStrikeOut;

                /// <summary>
                /// キャラクタセット。
                /// </summary>
                public byte lfCharSet;

                /// <summary>
                /// 出力精度。
                /// </summary>
                public byte lfOutPrecision;

                /// <summary>
                /// クリッピングの精度。
                /// </summary>
                public byte lfClipPrecision;

                /// <summary>
                /// 出力品質。
                /// </summary>
                public byte lfQuality;

                /// <summary>
                /// ピッチとファミリ。
                /// </summary>
                public byte lfPitchAndFamily;

                /// <summary>
                /// フォント名。
                /// </summary>
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
                public string lfFaceName;
            }
            #endregion

            /// <summary>
            /// 変換ウィンドウに表示する文字に使う論理フォントを設定する。
            /// </summary>
            /// <param name="hIMC">入力コンテキストのハンドル。</param>
            /// <param name="font">設定するフォント。</param>
            /// <returns>関数が成功した場合 true、失敗した場合 false。</returns>
            [DllImport("imm32.dll", CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool ImmSetCompositionFont(IntPtr hIMC, ref LOGFONT font);
		    
            /// <summary>
            /// 変換ウィンドウに表示する文字に使う論理フォントを設定する。
            /// </summary>
            /// <param name="hIMC">入力コンテキストのハンドル。</param>
            /// <param name="family">表示するフォントの <see cref="FontFamily"/>。</param>
            /// <param name="characterBounds">表示する文字のサイズ。</param>
            /// <returns>関数が成功した場合 true、失敗した場合 false。</returns>
            public static bool SetCompositionFont(IntPtr hIMC, FontFamily family, Rect characterBounds)
            {
                var lf = new LOGFONT();
                lf.lfFaceName = family.Source;
                lf.lfHeight = (int)characterBounds.Height;

                return ImmSetCompositionFont(hIMC, ref lf);
            }

            #endregion

            #region NotifyIme

            /// <summary>
            /// IME に変換文字列に対する処理を実行するように指示する。
            /// </summary>
            private const int NI_COMPOSITIONSTR = 0x15;

            /// <summary>
            /// 変換文字列を消去し状態を変換文字列なしに設定する。
            /// </summary>
            private const int CPS_CANCEL = 0x4;

            /// <summary>
            /// 入力コンテキストの状態の変更に関する情報を IME に通知する。
            /// </summary>
            /// <param name="hIMC">入力コンテキストのハンドル。</param>
            /// <param name="dwAction">通知コード。</param>
            /// <param name="dwIndex">候補一覧のインデックス。</param>
            /// <param name="dwValue">dwAction に指定した値に応じて候補文字列のインデックスを指定するか、または何も指定しない。</param>
            /// <returns>関数が成功した場合 true、失敗した場合 false。</returns>
            [DllImport("imm32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool ImmNotifyIME(IntPtr hIMC, int dwAction, int dwIndex, int dwValue = 0);

            /// <summary>
            /// 変換をキャンセルするよう IME に通知する。
            /// </summary>
            /// <param name="hIMC">入力コンテキストのハンドル。</param>
            /// <returns>関数が成功した場合 true、失敗した場合 false。</returns>
            public static bool NotifyIme(IntPtr hIMC)
            {
                return ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL);
            }

            #endregion

            #region GetTextFrameworkThreadManager

            #region ITfThreadMgr
            /// <summary>
            /// Text Service Framework Manager によって実装される基本的なオブジェクトを定義する。
            /// </summary>
            [ComImport, Guid("aa80e801-2021-11d2-93e0-0060b067b86e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            internal interface ITfThreadMgr
            {
                /// <summary>
                /// Text Service Framework を有効化する。
                /// </summary>
                /// <param name="clientId">クライアント ID。</param>
                void Activate(out int clientId);

                /// <summary>
                /// Text Service Framework を無効化する。
                /// </summary>
                void Deactivate();

                /// <summary>
                /// Document Manager オブジェクトを生成する。
                /// </summary>
                /// <param name="docMgr">Document Manager オブジェクト。</param>
                void CreateDocumentMgr(out object docMgr);

                /// <summary>
                /// すべての Document Manager を列挙するオブジェクトを取得する。
                /// </summary>
                /// <param name="enumDocMgrs">IEnumTfDocumentMgrs オブジェクト。</param>
                void EnumDocumentMgrs(out object enumDocMgrs);

                /// <summary>
                /// 現在フォーカスされている Document Manager を取得する。
                /// </summary>
                /// <param name="docMgr">現在フォーカスされている Document Manager。</param>
                void GetFocus(out object docMgr);

                /// <summary>
                /// 指定した Document Manager にフォーカスを設定する。
                /// </summary>
                /// <param name="docMgr">フォーカスする Document Manager。</param>
                void SetFocus(object docMgr);

                /// <summary>
                /// ウィンドウと Document Manager にフォーカスを関連付ける。
                /// </summary>
                /// <param name="hwnd">フォーカスするウィンドウのハンドル。</param>
                /// <param name="newDocMgr">フォーカスする Document Manager。</param>
                /// <param name="prevDocMgr">直前にフォーカスされていた Document Manager。</param>
                void AssociateFocus(IntPtr hwnd, object newDocMgr, out object prevDocMgr);

                /// <summary>
                /// 呼び出し元のスレッドがフォーカスを所有しているかどうかを示す値を取得する。
                /// </summary>
                /// <param name="isFocus">呼び出し元のスレッドがフォーカスを所有しているかどうかを示す値。</param>
                void IsThreadFocus([MarshalAs(UnmanagedType.Bool)] out bool isFocus);

                /// <summary>
                /// Function Provider オブジェクトを取得する。
                /// </summary>
                /// <param name="classId">Function Provider のクラス ID。</param>
                /// <param name="funcProvider">ITfFunctionProvider オブジェクト。</param>
                void GetFunctionProvider(ref Guid classId, out object funcProvider);

                /// <summary>
                /// すべての Function Provider を列挙するオブジェクトを取得する。
                /// </summary>
                /// <param name="enumProviders">IEnumTfFunctionProviders オブジェクト。</param>
                void EnumFunctionProviders(out object enumProviders);

                /// <summary>
                /// Global Compartment Manager を取得する。
                /// </summary>
                /// <param name="compartmentMgr">Global Compartment Manager オブジェクト。</param>
                void GetGlobalCompartment(out object compartmentMgr);
            }
            #endregion

            /// <summary>
            /// Thread Manager オブジェクトを生成する。
            /// </summary>
            /// <param name="threadManager">Thread Manager を受け取るためのポインタ。</param>
            /// <returns>処理結果のステータス。</returns>
            [DllImport("msctf.dll")]
            private static extern int TF_CreateThreadMgr(out ITfThreadMgr threadManager);

            /// <summary>
            /// Thread Manager が初期化済みかどうかを示す値。
            /// </summary>
            [ThreadStatic]
            private static bool textFrameworkThreadManagerInitialized;

            /// <summary>
            /// Thread Manager。
            /// </summary>
            [ThreadStatic]
            private static ITfThreadMgr textFrameworkThreadManager;

            /// <summary>
            /// Thread Manager オブジェクトを取得する。
            /// </summary>
            /// <returns>Thread Manager オブジェクト。</returns>
            public static ITfThreadMgr GetTextFrameworkThreadManager()
            {
                if (!textFrameworkThreadManagerInitialized)
                {
                    textFrameworkThreadManagerInitialized = true;
                    TF_CreateThreadMgr(out textFrameworkThreadManager);
                }

                return textFrameworkThreadManager;
            }

            #endregion
        }
        #endregion
    }
    #endregion

    #region MaskingItem
    /// <summary>
    /// <see cref="TextEditor"/> 上でマスキング表示する文字列の情報。
    /// </summary>
    public struct MaskingItem
    {
        #region MaskPattern
        /// <summary>
        /// マスクする文字列を取得、または設定する。<br />
        /// 正規表現での指定も可能。
        /// </summary>
        public string MaskPattern { get; set; }
        #endregion

        #region MaskingChar
        /// <summary>
        /// <see cref="MaskPattern"/> で指定した文字列を置き換えて表示する文字を取得、または設定する。
        /// </summary>
        public char MaskingChar { get; set; }
        #endregion
    }
    #endregion

    #region MatchCollectionExtensions
    /// <summary>
    /// <see cref="MatchCollection"/> の拡張メソッド群。
    /// </summary>
    internal static class MatchCollectionExtensions
    {
        #region AsEnumerable
        /// <summary>
        /// <see cref="MatchCollection"/> を列挙するための拡張メソッド。
        /// </summary>
        /// <param name="collection">ソースコレクション。</param>
        /// <returns><see cref="Match"/> オブジェクト。</returns>
        public static IEnumerable<Match> AsEnumerable(this MatchCollection collection)
        {
            var @enum = collection.GetEnumerator();
            while (@enum.MoveNext())
                yield return @enum.Current as Match;
        }
        #endregion
    }
    #endregion
}
